#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#define MSG_HEADER_LENGTH (9)

#pragma pack(push, 1)
typedef struct
{
    uint32_t body_received;
    uint8_t type;
    uint32_t sn;
    uint32_t length;
    char *body;
} msg_t;
#pragma pack(pop)

char *msg_body(msg_t *msg) {
    return (char *)msg + offsetof(msg_t, body);
}

char *msg_stream(msg_t *msg) {
    return (char *)msg + sizeof(uint32_t);
}

msg_t *msg_parse(char *stream) {
    msg_t *msg = (msg_t *)calloc(1, sizeof(msg_t));

    if (msg) {
        memcpy(msg_stream(msg), stream, MSG_HEADER_LENGTH);

        if (msg->length > 0) {
            msg = (msg_t *)realloc(msg, 4 + MSG_HEADER_LENGTH + msg->length + 1);
            if (msg) {
                memcpy(msg_body(msg), stream + MSG_HEADER_LENGTH, msg->length);
                msg->body_received = msg->length;
            } else {
                free(msg);
                msg = NULL;
            }
        }
    }

    return msg;
}

void msg_print(msg_t *msg) {
    printf("======TYPE: %c===============\n", msg->type + 0x30);
    printf("\t[   SN   ]: %d\n", msg->sn);
    printf("\t[ LENGTH ]: %d ( recved: %d )\n", msg->length, msg->body_received);
    printf("\t[  BODY  ]: %s\n", msg->length ? msg_body(msg) : "NULL");
    printf("===========================\n");
}

msg_t *msg_create(uint8_t type, uint32_t sn, uint32_t body_len, char *body)
{
    msg_t *msg = (msg_t *)calloc(1, 4 + MSG_HEADER_LENGTH + body_len + 1);
    if (msg) {
        msg->body_received = body_len;
        msg->type = type;
        msg->sn = sn;
        msg->length = body_len;

        if (body_len > 0) {
            memcpy(msg_body(msg), body, body_len);
        }
    }

    return msg;
}

void msg_free(msg_t *msg) {
    if (msg) {
        free(msg);
    }

    msg = NULL;
}

int main(int argc, char **argv) {
    int ret = -1;
    msg_t *msg = NULL;
    msg_t *msg2 = NULL;

    char *data = "Hello msg";
    printf("[ I ] begin cp data: %s, len: %zd...\n", data, strlen(data));

    msg = msg_create(1, 101, strlen(data), data);
    if (!msg) {
        fprintf(stderr, "[ E ] failed create msg!\n");
        goto err;
    }

    msg_print(msg);

    char *stream = msg_stream(msg);
    msg2 = msg_parse(stream);

    if (!msg2) {
        fprintf(stderr, "[ E ] failed parse msg2 from msg!\n");
        goto err;
    }

    msg_print(msg2);

    ret = 0;
err:
    if (msg) msg_free(msg);
    if (msg2) msg_free(msg2);
    return ret;
}
